const onRE = /^on[^a-z]/;
const isOn = (key) => onRE.test(key);

const options = {
  createElement(tag) {
    return document.createElement(tag);
  },
  setElementText(el, text) {
    el.textContent = text;
  },
  insert(el, parent, anchor = null) {
    parent.insertBefore(el, anchor);
  },
  patchProps(el, key, prevValue, nextValue) {
    if (isOn(key)) {
      const eventName = key.slice(2).toLowerCase();

      //_vei其实就是vue event invoker的简写，就是一个缓存
      let invokers = el._vei || (el._vei = {});

      // 通过事件名称获取对应的函数
      let invoker = invokers[key];

      if (nextValue) {
        if (!invoker) {
          invoker = el._vei[key] = (e) => {
            // 如果触发事件，早于事件处理函数的绑定时间，就不执行
            if (e.timeStamp < invoker.attached) return;
            // 如果invoker.value是数组，那么就遍历数组，依次执行
            if (Array.isArray(invoker.value)) {
              invoker.value.forEach((fn) => fn(e));
            } else {
              invoker.value(e);
            }
          };
          invoker.value = nextValue;
          // 添加一个存储时间的属性
          invoker.attached = performance.now();
          el.addEventListener(eventName, invoker);
        } else {
          // 如果invoker存在，说明是更新，直接给内部的invoker.value重新赋值
          invoker.value = nextValue;
        }
      } else {
        el.removeEventListener(eventName, invoker);
      }
    } else if (key === "class") {
      el.className = nextValue || "";
    } else if (shouldSetAsProps(el, key, nextValue)) {
      const type = typeof el[key];

      if (type === "boolean" && nextValue === "") {
        el[key] = true;
      } else {
        el[key] = nextValue;
      }
    } else {
      el.setAttribute(key, nextValue);
    }
  },
};

function shouldSetAsProps(el, key, value) {
  if (key === "form" && el.tagName === "INPUT") {
    return false;
  }

  return key in el;
}

// container.innerHTML = "" 清空内容，会存在一些问题
// 在卸载的时候，可能会调用一些其他钩子函数，比如beforeUnmount等生命周期钩子函数
// 组件上可能会存在自定义指令，卸载的时候我们需要正确的去执行这些指令钩子函数
// container.innerHTML = "" 清空内容,不会自动的清空DOM元素上绑定的事件处理函数

// 最好我们通过dom方式给清除掉，而且应该有具体的处理函数

function createRenderer(options) {
  const { createElement, setElementText, insert, patchProps } = options;

  function unmount(vnode) {
    const parent = vnode.el.parentNode;
    if (parent) {
      parent.removeChild(vnode.el);
    }
  }

  function render(vnode, container) {
    if (vnode) {
      patch(container._vnode, vnode, container);
    } else {
      if (container._vnode) {
        unmount(container._vnode);
      }
    }

    container._vnode = vnode;
  }

  function patchElement(oldVnode, newVnode) {
    const el = (newVnode.el = oldVnode.el),
      oldProps = oldVnode.props,
      newProps = newVnode.props;

    // 更新props，如果新的虚拟节点和旧的虚拟节点中属性不一样，进行替换
    for (const key in newProps) {
      if (newProps[key] !== oldProps[key]) {
        patchProps(el, key, oldProps[key], newProps[key]);
      }
    }

    // 如果旧节点中的属性，新节点中没有，将属性值设置为null
    for (const key in oldProps) {
      if (!(key in newProps)) {
        patchProps(el, key, oldProps[key], null);
      }
    }

    //先做属性更新，todo:子节点更新
    // .......
  }

  function patch(oldVnode, newVnode, container) {
    // 如果旧节点的type和新节点的type不一样，直接卸载旧节点，挂载新节点
    if (oldVnode && oldVnode.type !== newVnode.type) {
      unmount(oldVnode);
      oldVnode = null;
    }

    const { type } = newVnode;

    if (typeof type === "string") {
      // 如果旧节点不存在，就意味着是挂载，调用mountElement函数完成挂载
      if (!oldVnode) {
        mountElement(newVnode, container);
      } else {
        // 如果oldVnode存在，就是更新, 暂时省略...
        console.log("打补丁");
        patchElement(oldVnode, newVnode);
      }
    } else if (typeof type === "object") {
      // todo: 组件处理
      console.log("组件处理");
    } else {
      // todo: 未知类型
      console.log("未知类型");
    }
  }

  function mountElement(vnode, container) {
    // 创建元素
    const el = (vnode.el = createElement(vnode.type));
    if (typeof vnode.children === "string") {
      // 如果子节点是字符串，说明是文本节点，直接挂载
      setElementText(el, vnode.children);
    } else if (Array.isArray(vnode.children)) {
      vnode.children.forEach((child) => {
        patch(null, child, el);
      });
    }

    if (vnode.props) {
      for (const key in vnode.props) {
        const value = vnode.props[key];
        patchProps(el, key, null, value);
      }
    }

    insert(el, container);
  }

  // 其他可能会用到的函数...
  return {
    render,
    //...其他的函数
  };
}

const isString = (val) => {
  return typeof val === "string";
};

const isObject = (val) => {
  return val !== null && typeof val === "object";
};

const isArray = Array.isArray;

function normalizeClass(value) {
  let res = "";

  if (isString(value)) {
    res = value;
  } else if (isArray(value)) {
    for (let i = 0; i < value.length; i++) {
      const normalized = normalizeClass(value[i]);
      if (normalized) {
        res += normalized + " ";
      }
    }
  } else if (isObject(value)) {
    for (const name in value) {
      if (value[name]) {
        res += name + " ";
      }
    }
  }

  return res;
}

// 事件冒泡
// const vnode = {
//   type: "div",
//   props: {
//     onClick: () => {
//       alert("父元素div click")
//     }
//   },
//   children: [
//     {
//       type: "p",
//       props: {
//         onClick: () => {
//           alert("子元素p click")
//         }
//       },
//       children: "text"
//     }
//   ]
// };

// const renderer = createRenderer(options);
// renderer.render(vnode, document.getElementById("app"));

const { effect, ref } = VueReactivity;
const flag = ref(false);

effect(() => {
  const vnode = {
    type: "div",
    props: flag.value
      ? {
          onClick: () => {
            alert("父元素div click");
          },
        }
      : {},
    children: [
      {
        type: "p",
        props: {
          onClick: () => {
            flag.value = true;
            alert("子元素p click");
          },
        },
        children: "text",
      },
    ],
  };

  const renderer = createRenderer(options);
  renderer.render(vnode, document.getElementById("app"));
});
